package cn.hchaojie.jfx.searcher;

import java.util.LinkedList;
import java.util.Random;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

// UI显示，如何显示各种ui元素：蛇、食物、背景的网格

// 动画
// 界面控制（W\A\S\D, UP\DOWN|LEFT\RIGHT）
// 碰撞检测（撞墙、撞自己、吃食物）
// 游戏的生命周期、状态控制（开始、结束、暂停、继续）

public class SnakeGame extends Application {
	// 网格的行列数
	private static final int COLS = 20;
	private static final int ROWS = 30;

	private static final int FPS = 5;

	// 网格容器
	private GridPane grid;

	// 随机位置生成器
	Random random = new Random();

	// 当前的蛇
	private Snake snake;
	
	// 当前方向
	private Direction direction;

	// 当前食物
	private Food food;
	private Timeline timeline;
	
	// 游戏当前状态
	private State state;

	@Override
	public void start(Stage primaryStage) throws Exception {
		Parent parent = createUi();
		Scene scene = new Scene(parent);
		
		scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
			@Override
			public void handle(KeyEvent event) {
				handleKeyEvent(event);
			}
		});
		
		primaryStage.setScene(scene);
		primaryStage.show();
	}

	// 处理键盘事件
	protected void handleKeyEvent(KeyEvent event) {
		switch (event.getCode()) {
		case UP:
		case W:
			changeDirection(Direction.UP);
			break;
			
		case DOWN:
		case S:
			changeDirection(Direction.DOWN);
			break;

		case LEFT:
		case A:
			changeDirection(Direction.LEFT);
			break;

		case RIGHT:
		case D:
			changeDirection(Direction.RIGHT);
			break;
			
		case SPACE:
			startGame();
			break;

		default:
			break;
		}
	}
	
	// 开始游戏
	private void startGame() {
		if (state == State.STARTED) {
			return;
		}
		
		// 初始化的时候开始游戏
		
		// 游戏结束时重新开始
		if (state == State.STOPPED) {
			// 把食物和蛇重新生成一遍
			food.removeFromGrid();
			randomFood();
			
			snake.removeFromGrid();
			createSnake();
		}
		
		state = State.STARTED;
		
		timeline.play();
	}

	// 改变方向, to: 变成什么方向
	private void changeDirection(Direction to) {
		if (state != State.STARTED) {
			return;
		}
		
		// 判断是否是反方向
		if (this.direction.isOpposite(to)) {
			return;
		}
		
		// direction -> to
		direction = to;
	}

	// 创建游戏界面
	private Parent createUi() {
		grid = new GridPane();
		grid.setVgap(2);
		grid.setHgap(2);
		
		for (int i = 0; i < ROWS; i++) {
			for (int j = 0; j < COLS; j++) {
				Block block = new Block(j, i);
				grid.add(block, j, i);
			}
		}
		
		// 随机产生食物
		randomFood();
		
		// 创建蛇
		createSnake();
		
		direction = Direction.UP;
		
		// 创建一个帧动画，让蛇自动走
		
		// 每秒钟动画播放一帧，需要在handle方法里面指定执行的代码
		KeyFrame keyFrame = new KeyFrame(Duration.seconds(1.0 / FPS), new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				move();		// 每秒往上走一步
			}
		});
		timeline = new Timeline(keyFrame);
		// timeline.setCycleCount(7);		// 动画执行5次
		timeline.setCycleCount(Timeline.INDEFINITE);
		
		state = State.INIT;
		
		return grid;
	}
	
	// 处理各方方向的走动
	public void move() {
		Block head = snake.blocks.getFirst();
		
		Body neck = new Body(head.x, head.y);
		snake.blocks.add(1, neck);
		neck.addToGrid();
		
		// 先从网格里面删除
		head.removeFromGrid();
		
		// 更新坐标
		switch (direction) {
		case UP:
			head.y--;
			break;
			
		case DOWN:
			head.y++;
			break;

		case LEFT:
			head.x--;
			break;

		case RIGHT:
			head.x++;
			break;
		}
		
		// 判断碰撞（撞墙、撞自己）
		if (hitWall() || hitSelf()) {
			gameOver();
			return;
		}
		
		// 重新添加到网格
		head.addToGrid();
		
		// 判断是否吃到食物
		if (head.x == food.x && head.y == food.y) {
			// 删除原来的食物
			food.removeFromGrid();
			
			// 生产新的食物
			randomFood();
		} else {
			// 删除尾巴
			Block tail = snake.blocks.removeLast();
			tail.removeFromGrid();
		}
	}

	private void gameOver() {
		System.out.println("Game Over");
		timeline.stop();
		
		state = State.STOPPED;
	}

	// 是否撞到自己
	private boolean hitSelf() {
		Block head = snake.blocks.getFirst();
		
		for (int i = 1; i < snake.blocks.size(); i++) {
			Block body = snake.blocks.get(i);
			
			if (head.x == body.x && head.y == body.y) {
				return true;
			}
		}
		
		return false;
	}

	// 是否撞墙
	private boolean hitWall() {
		Block head = snake.blocks.getFirst();
		
		return head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS;
	}

	private void createSnake() {
		snake = new Snake();
		snake.addBlock(new Head(15, 9));
		snake.addBlock(new Body(15, 10));
		snake.addBlock(new Body(15, 11));
		snake.addBlock(new Body(15, 12));
		snake.addBlock(new Body(15, 13));
		
		snake.addToGrid();
	}

	// 随机产生食物
	public void randomFood() {
		int y = random.nextInt(30);		// [0, 30)
		int x = random.nextInt(20);		// [0, 20)

		food = new Food(x, y);
		food.addToGrid();
	}

	public static void main(String[] args) {
		launch(args);
	}
	
	// 代表界面上一个小格子
	class Block extends Rectangle {
		public int x;			// 列号
		public int y;			// 行号
		public Color color;	// 格子颜色

		public Block(int x, int y) {
			this(x, y, Color.PINK);
		}
		
		public Block(int x, int y, Color color) {
			super(20, 20, color);
			
			setArcHeight(10);
			setArcWidth(10);
			
			this.x = x;
			this.y = y;
			this.color = color;
		}
		
		// 将小格子显示到网格
		public void addToGrid() {
			grid.add(this, x, y);
		}
		
		// 将小格子从网格里面删除
		public void removeFromGrid() {
			grid.getChildren().remove(this);
		}
	}
	
	// 食物
	class Food extends Block {
		public Food(int x, int y) {
			super(x, y, Color.RED);
		}
	}
	
	// 蛇头
	class Head extends Block {
		public Head(int x, int y) {
			super(x, y, Color.GREEN);
		}
	}
	
	// 蛇身
	class Body extends Block {
		public Body(int x, int y) {
			super(x, y, Color.INDIGO);
		}
	}
	
	// 蛇
	class Snake {
		LinkedList<Block> blocks = new LinkedList<>();
		
		public void addBlock(Block block) {
			blocks.add(block);
		}
		
		// 将蛇添加到网格
		public void addToGrid() {
			for (Block b : blocks) {
				b.addToGrid();
			}
		}
		
		public void removeFromGrid() {
			for (Block b : blocks) {
				b.removeFromGrid();
			}
		}
	}
	
	// 方向的枚举
	enum Direction {
		UP, DOWN, LEFT, RIGHT;
		
		public boolean isOpposite(Direction to) {
			return (this == Direction.DOWN && to == Direction.UP) 
			|| (this == Direction.UP && to == Direction.DOWN)
			|| (this == Direction.LEFT && to == Direction.RIGHT)
			|| (this == Direction.RIGHT && to == Direction.LEFT);
		}
	}
	
	// 游戏状态
	enum State {
		// 初始化，运行中，结束
		INIT, STARTED, STOPPED
	}
}
